package com.billion.payment.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.billion.common.utils.IpUtils;
import com.billion.payment.config.WxpayConfig;
import com.billion.payment.entity.weixin.WxOrderEntity;
import com.billion.payment.entity.weixin.WxPayEntity;
import com.billion.payment.entity.weixin.WxRefundEntity;
import com.billion.payment.service.IWxpayService;
import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class WxpayServiceImpl implements IWxpayService {

    private WxPayService wxPayService;

    /**
     * 构建支付参数
     * @param subAppId
     * @param subMchId
     * @return
     */
    private WxPayConfig createWxPayConfig(String subAppId, String subMchId){
        WxPayConfig wxPayConfig = new WxPayConfig();
        wxPayConfig.setAppId(WxpayConfig.getAppId());
        wxPayConfig.setMchId(WxpayConfig.getMchId());
        wxPayConfig.setMchKey(WxpayConfig.getMchKey());
        wxPayConfig.setKeyPath(WxpayConfig.getKeyPath());
        wxPayConfig.setNotifyUrl(WxpayConfig.getNotifyUrl());
        wxPayConfig.setSubAppId(subAppId);
        wxPayConfig.setSubMchId(subMchId);
        return wxPayConfig;
    }

    /**
     * 支付统一下单
     * @param wxOrderEntity
     * @return
     */
    @Override
    public JSONObject unifiedOrder(WxOrderEntity wxOrderEntity) {
        JSONObject jsonObject = new JSONObject();

        /**
         * 构建统一支付参数
         */
        WxPayConfig wxPayConfig = this.createWxPayConfig(wxOrderEntity.getSubAppId(), wxOrderEntity.getSubMchId());
        wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(wxPayConfig);

        /**
         * 构建统一下单参数
         */
        WxPayUnifiedOrderRequest unifiedOrderRequest = new WxPayUnifiedOrderRequest();
        unifiedOrderRequest.setDeviceInfo(wxOrderEntity.getDeviceInfo());
        unifiedOrderRequest.setNonceStr(String.valueOf(System.currentTimeMillis()));
        unifiedOrderRequest.setBody(wxOrderEntity.getBody());
        unifiedOrderRequest.setOutTradeNo(wxOrderEntity.getOutTradeNo());
        unifiedOrderRequest.setTotalFee(wxOrderEntity.getTotalFee());
        unifiedOrderRequest.setSpbillCreateIp(IpUtils.getHostIp());
        unifiedOrderRequest.setTradeType(wxOrderEntity.getTradeType());
        unifiedOrderRequest.setOpenid(wxOrderEntity.getOpenId());
        unifiedOrderRequest.setSubOpenid(wxOrderEntity.getSubOpenId());
        try {
            /**
             * 微信下单
             */
            WxPayUnifiedOrderResult unifiedOrderResult = wxPayService.unifiedOrder(unifiedOrderRequest);
            if("SUCCESS".equals(unifiedOrderResult.getReturnCode())){
                if("SUCCESS".equals(unifiedOrderResult.getResultCode())){
                    jsonObject.put("prepay_id", unifiedOrderResult.getPrepayId());
                    jsonObject.put("key", wxPayConfig.getMchKey());
                    jsonObject.put("appid", unifiedOrderResult.getSubAppId());
                    jsonObject.put("nonce_str", unifiedOrderResult.getNonceStr());
                    jsonObject.put("code_url", unifiedOrderResult.getCodeURL());
                    jsonObject.put("code", "success");
                }else{
                    jsonObject.put("code", unifiedOrderResult.getErrCode());
                    jsonObject.put("msg", unifiedOrderResult.getErrCodeDes());
                }
            }else{
                jsonObject.put("code", unifiedOrderResult.getReturnCode());
                jsonObject.put("msg", unifiedOrderResult.getReturnMsg());
            }
        } catch (WxPayException e) {
            e.printStackTrace();
            jsonObject.put("code", e.getErrCode());
            jsonObject.put("msg", e.getErrCodeDes());
            log.error("统一下单失败" + e.getErrCodeDes());
        }
        return jsonObject;
    }

    /**
     * 扫码支付
     * @param wxPayEntity
     * @return
     */
    @Override
    public JSONObject microPay(WxPayEntity wxPayEntity) {
        JSONObject jsonObject = new JSONObject();

        /**
         * 构建统一支付参数
         */
        WxPayConfig wxPayConfig = this.createWxPayConfig(wxPayEntity.getSubAppId(), wxPayEntity.getSubMchId());
        wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(wxPayConfig);

        /**
         * 构建付款码支付参数
         */
        WxPayMicropayRequest micropayRequest = new WxPayMicropayRequest();
        micropayRequest.setDeviceInfo(wxPayEntity.getDeviceInfo());
        micropayRequest.setNonceStr(String.valueOf(System.currentTimeMillis()));
        micropayRequest.setBody(wxPayEntity.getBody());
        micropayRequest.setOutTradeNo(wxPayEntity.getOutTradeNo());
        micropayRequest.setTotalFee(wxPayEntity.getTotalFee());
        micropayRequest.setSpbillCreateIp(IpUtils.getHostIp());
        micropayRequest.setAuthCode(wxPayEntity.getAuthCode());
        try {
            /**
             * 获取付款码付款返回结果
             */
            WxPayMicropayResult micropayResult = wxPayService.micropay(micropayRequest);
            if("SUCCESS".equals(micropayResult.getReturnCode())){
                String outTradeNo = micropayResult.getOutTradeNo();
                String tradeNo = micropayResult.getTransactionId();
                if("SUCCESS".equals(micropayResult.getResultCode())){
                    jsonObject.put("outTradeNo", outTradeNo);
                    jsonObject.put("tradeNo", tradeNo);
                    jsonObject.put("code", "success");
                }else if("USERPAYING".equals(micropayResult.getErrCode())){
                    boolean flag = true;
                    int count = 0;
                    while (flag){
                        try {
                            Thread.sleep(5000L);
                            WxPayOrderQueryResult orderQueryResult = wxPayService.queryOrder(tradeNo, outTradeNo);
                            if("SUCCESS".equals(orderQueryResult.getTradeState())){
                                jsonObject = new JSONObject();
                                jsonObject.put("outTradeNo", outTradeNo);
                                jsonObject.put("tradeNo", tradeNo);
                                flag = false;
                            }
                        } catch (InterruptedException e) {
                            jsonObject.put("code", "0");
                            jsonObject.put("msg", "交易失败");
                            e.printStackTrace();
                            flag = false;
                        } finally {
                            count ++;
                            if(count > 20){
                                jsonObject.put("code", "0");
                                jsonObject.put("msg", "交易超时，订单关闭");
                                wxPayService.closeOrder(outTradeNo);
                                flag = false;
                            }
                        }
                    }
                }
            }else{
                jsonObject.put("code", micropayResult.getReturnCode());
                jsonObject.put("msg", micropayResult.getReturnMsg());
            }
        } catch (WxPayException e) {
            e.printStackTrace();
            jsonObject.put("code", e.getErrCode());
            jsonObject.put("msg", e.getErrCodeDes());
            log.error("扫码支付失败" + e.getErrCodeDes());
        }
        return jsonObject;
    }

    /**
     * 微信退款
     * @param wxRefundEntity
     * @return
     */
    @Override
    public JSONObject refund(WxRefundEntity wxRefundEntity) {
        JSONObject jsonObject = new JSONObject();

        /**
         * 构建微信统一支付参数
         */
        WxPayConfig wxPayConfig = this.createWxPayConfig(wxRefundEntity.getSubAppId(), wxRefundEntity.getSubMchId());
        wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(wxPayConfig);

        /**
         * 构建退款参数
         */
        WxPayRefundRequest refundRequest = new WxPayRefundRequest();
        refundRequest.setNonceStr(String.valueOf(System.currentTimeMillis()));
        refundRequest.setTransactionId(wxRefundEntity.getTransactionId());
        refundRequest.setOutTradeNo(wxRefundEntity.getOutTradeNo());
        refundRequest.setOutRefundNo(wxRefundEntity.getOutRefundNo());
        refundRequest.setTotalFee(wxRefundEntity.getTotalFee());
        refundRequest.setRefundFee(wxRefundEntity.getRefundFee());
        refundRequest.setRefundDesc(wxRefundEntity.getRefundDesc());
        try {
            WxPayRefundResult refundResult = wxPayService.refund(refundRequest);
            if("SUCCESS".equals(refundResult.getReturnCode())){
                String outTradeNo = refundResult.getOutRefundNo();
                String tradeNo = refundResult.getRefundId();
                if("SUCCESS".equals(refundResult.getResultCode())){
                    jsonObject.put("outTradeNo", outTradeNo);
                    jsonObject.put("tradeNo", tradeNo);
                    jsonObject.put("code", "success");
                }
            }else{
                jsonObject.put("code", refundResult.getReturnCode());
                jsonObject.put("msg", refundResult.getReturnMsg());
            }
        } catch (WxPayException e) {
            e.printStackTrace();
            jsonObject.put("code", e.getErrCode());
            jsonObject.put("msg", e.getErrCodeDes());
            log.error("退费失败" + e.getErrCodeDes());
        }
        return jsonObject;
    }

    /**
     * 获取微信openid
     * @param appId
     * @param secret
     * @param code
     * @return
     */
    @Override
    public String getOpenId(String appId, String secret, String code) {
        String openid = "";
        try {
            String url = "https://api.weixin.qq.com/sns/jscode2session";
            url += "?appid=" + appId;
            url += "&secret=" + secret;
            url += "&js_code=" + code;
            url += "&grant_type=authorization_code";
            url += "&connect_redirect=1";
            String res = null;
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            HttpGet httpget = new HttpGet(url);
            CloseableHttpResponse response = null;
            // 配置信息
            RequestConfig requestConfig = RequestConfig.custom()          // 设置连接超时时间(单位毫秒)
                    .setConnectTimeout(5000)                    // 设置请求超时时间(单位毫秒)
                    .setConnectionRequestTimeout(5000)             // socket读写超时时间(单位毫秒)
                    .setSocketTimeout(5000)                    // 设置是否允许重定向(默认为true)
                    .setRedirectsEnabled(false).build();           // 将上面的配置信息 运用到这个Get请求里
            httpget.setConfig(requestConfig);                         // 由客户端执行(发送)Get请求
            response = httpClient.execute(httpget);                   // 从响应模型中获取响应实体
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                res = EntityUtils.toString(responseEntity);
            }
            // 释放资源
            if (httpClient != null) {
                httpClient.close();
            }
            if (response != null) {
                response.close();
            }
            JSONObject jsonObject = JSON.parseObject(res);
            openid = jsonObject.getString("openid");
        }catch (Exception e){
            e.printStackTrace();
        }
        return openid;
    }
}
